/*
+--------------------------
| FreeloaderOS v0.0.1
|  - Microkernel
+--------------------------
| Written by Alec Hussey
| Copyright (c) 2007
|  - Deus X Foundation
+---------------------------
*/

/*
 * Interrupt Ruquests Controller
 */

#include <sys/system.h>
#include <sys/irqs.h>

/*
Install a custom IRQ handler for
a given IRQ.
*/
inline void irq_install_handler(int irq, void (*handler)(struct regs *r))
{
	irq_routines[irq] = handler;
}

/*
Clear or delete a given IRQ handler
*/
inline void irq_uninstall_handler(int irq)
{
	irq_routines[irq] = 0;
}

/*
Remap IRQ table to prevent a double fault
*/
inline void irq_remap(void)
{
	outportb(0x20, 0x11);
	outportb(0xA0, 0x11);
	outportb(0x21, 0x20);
	outportb(0xA1, 0x28);
	outportb(0x21, 0x04);
	outportb(0xA1, 0x02);
	outportb(0x21, 0x01);
	outportb(0xA1, 0x01);
	outportb(0x21, 0x0);
	outportb(0xA1, 0x0);
}

/*
First remap the interrupt controllers
then install the correct ISRs into the IDT
*/
void init_irq()
{
	irq_remap();
	idt_set_gate(32, (unsigned)irq0, 0x08, 0x8E);
	idt_set_gate(33, (unsigned)irq1, 0x08, 0x8E);
	idt_set_gate(34, (unsigned)irq2, 0x08, 0x8E);
	idt_set_gate(35, (unsigned)irq3, 0x08, 0x8E);
	idt_set_gate(36, (unsigned)irq4, 0x08, 0x8E);
	idt_set_gate(37, (unsigned)irq5, 0x08, 0x8E);
	idt_set_gate(38, (unsigned)irq6, 0x08, 0x8E);
	idt_set_gate(39, (unsigned)irq7, 0x08, 0x8E);
	idt_set_gate(40, (unsigned)irq8, 0x08, 0x8E);
	idt_set_gate(41, (unsigned)irq9, 0x08, 0x8E);
	idt_set_gate(42, (unsigned)irq10, 0x08, 0x8E);
	idt_set_gate(43, (unsigned)irq11, 0x08, 0x8E);
	idt_set_gate(44, (unsigned)irq12, 0x08, 0x8E);
	idt_set_gate(45, (unsigned)irq13, 0x08, 0x8E);
	idt_set_gate(46, (unsigned)irq14, 0x08, 0x8E);
	idt_set_gate(47, (unsigned)irq15, 0x08, 0x8E);
}

/*
Take each IRQ ISR poinrt and signal them
when we are done servicing them.
*/
void irq_handler(struct regs *r)
{
	// Blank function pointer
	void (*handler)(struct regs *r);
	
	// Check if there is a custom handler
	// to run for this
	handler = irq_routines[r->int_no - 32];
	if (handler)
		handler(r);
	
	// Check if IDT entry it equal or over 40
	// and if so, send an EOI (End of Interrupt)
	// to the slave controller.
	if (r->int_no >= 40)
		outportb(0xA0, 0x20);
	
	// Now send an EOI signal to the master
	// controller.
	outportb(0x20, 0x20);
}
